﻿// XmlDesignerDlg.cpp: 实现文件
//

#include "stdafx.h"
#include "DuilibHelper.h"
#include "XmlDesignerDlg.h"
#include "afxdialogex.h"
#include "ControlChoiceDlg.h"
#include "ControllerConfigDlg.h"


// XmlDesignerDlg 对话框

IMPLEMENT_DYNAMIC(XmlDesignerDlg, CDialogEx)

XmlDesignerDlg::XmlDesignerDlg(CString loadFilePath /*= L""*/): file_path_(loadFilePath)
{

}

XmlDesignerDlg::~XmlDesignerDlg()
{

}

void XmlDesignerDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialogEx::DoDataExchange(pDX);
  DDX_Control(pDX, IDC_TREE_NODE, node_tree_);
  DDX_Control(pDX, IDC_LIST_ATTR_DESC, attr_desc_list_);
  DDX_Control(pDX, IDC_EDIT_VALUE, value_edit_);
  DDX_Control(pDX, IDC_STATIC_ATTR_NAME, name_label_);
  DDX_Control(pDX, IDC_BUTTON_UPDATE, update_btn_);
}


BEGIN_MESSAGE_MAP(XmlDesignerDlg, CDialogEx)
  ON_NOTIFY(NM_CLICK, IDC_TREE_NODE, &XmlDesignerDlg::OnTreeClick)
  ON_NOTIFY(NM_RCLICK, IDC_TREE_NODE, &XmlDesignerDlg::OnTreeRClick)
  ON_NOTIFY(TVN_KEYDOWN, IDC_TREE_NODE, &XmlDesignerDlg::OnTreeKeyDown)
  ON_COMMAND(ID_DELETE, &XmlDesignerDlg::OnMenuDelete)
  ON_COMMAND(ID_DOWN, &XmlDesignerDlg::OnMenuMoveDown)
  ON_COMMAND(ID_UP, &XmlDesignerDlg::OnMenuMoveUp)
  ON_COMMAND(ID_INSERT, &XmlDesignerDlg::OnMenuInsertChild)
  ON_BN_CLICKED(IDC_BUTTON_SHOW, &XmlDesignerDlg::OnXmlShow)
  ON_BN_CLICKED(IDC_BUTTON_OUTPUT, &XmlDesignerDlg::OnXmlGenerate)
  ON_BN_CLICKED(IDC_BUTTON_UPDATE, &XmlDesignerDlg::OnAttrValueUpdate)
  ON_NOTIFY(NM_CLICK, IDC_LIST_ATTR_DESC, &XmlDesignerDlg::OnAttrValueListClick)
  ON_NOTIFY(NM_KILLFOCUS, IDC_LIST_ATTR_DESC, &XmlDesignerDlg::OnAttrValueListKillFocus)
  ON_NOTIFY(NM_SETFOCUS, IDC_LIST_ATTR_DESC, &XmlDesignerDlg::OnAttrValueListSetFocus)
  ON_WM_KEYDOWN()
END_MESSAGE_MAP()



BOOL XmlDesignerDlg::OnInitDialog()
{
  CDialogEx::OnInitDialog();

  SetupUI();

  if (file_path_.IsEmpty()) { //如果是新建xml文件
    xml_node pre = doc_.prepend_child(pugi::node_declaration);
    pre.append_attribute(L"version") = L"1.0";
    pre.append_attribute(L"encoding") = L"utf-8";
    xml_node window_node = doc_.append_child(_T("Window"));
    node_map[node_tree_.InsertItem(_T("Window"), NULL)] = window_node;

    window_node.append_child(_T("Include")).append_attribute(_T("source")) = _T("default.xml");
    window_node.append_child(_T("Include")).append_attribute(_T("source")) = _T("font.xml");
    window_node.append_child(_T("Include")).append_attribute(_T("source")) = _T("lang.xml");

    window_node.append_attribute(L"size") = _T("500,400");
  } else {
    xml_parse_result ret = doc_.load_file(file_path_.GetBuffer());
    xml_node node_window = doc_.child(L"Window");

    while (node_window.remove_child(_T("Include")));
    node_window.insert_child_before(_T("Include"), node_window.first_child()).append_attribute(_T("source")) = _T("default.xml");
    node_window.insert_child_before(_T("Include"), node_window.first_child()).append_attribute(_T("source")) = _T("font.xml");
    node_window.insert_child_before(_T("Include"), node_window.first_child()).append_attribute(_T("source")) = _T("lang.xml");

    HTREEITEM root = node_tree_.InsertItem(_T("Window"), NULL);
    node_map[root] = node_window;
    LoadChildNode(root, node_window);
  }

  return TRUE;
}

void XmlDesignerDlg::LoadChildNode(HTREEITEM parent, xml_node& parent_node)
{
  for (auto it : parent_node.children()) {
    if (GLOBAL()->controller_attr_mp_.find(it.name()) != GLOBAL()->controller_attr_mp_.end()) {
      HTREEITEM item = node_tree_.InsertItem(it.name(), parent);
      node_map[item] = it;
      if (!it.first_child().empty())
        LoadChildNode(item, it);
    }
  }
}

void XmlDesignerDlg::SetupUI()
{
  //设置树形控件
  DWORD dwStyle = node_tree_.GetStyle();
  dwStyle |= TVS_TRACKSELECT | TVS_SHOWSELALWAYS;
  node_tree_.ModifyStyle(0, dwStyle);
  //设置列表控件
  dwStyle = attr_desc_list_.GetExtendedStyle();
  dwStyle |= LVS_EX_SINGLEROW | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT;
  attr_desc_list_.SetExtendedStyle(dwStyle);
  RECT rec;
  attr_desc_list_.GetClientRect(&rec);
  attr_desc_list_.InsertColumn(0, _T("属性名"), LVCFMT_LEFT, 100);
  attr_desc_list_.InsertColumn(1, _T("设置值"), LVCFMT_LEFT, rec.right - rec.left - 100);

  attr_desc_list_.EnableWindow(FALSE);
}


void XmlDesignerDlg::SetSaveFileName()
{
  CFileDialog saveFileDlg(FALSE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("文件 (*.xml)|*.xml||"), NULL);
  if (saveFileDlg.DoModal() == IDOK) {
    file_path_ = saveFileDlg.GetPathName();
    fs::path p = file_path_.GetBuffer();
    if (fs::path{ file_path_.GetBuffer() }.extension().empty()) {
      file_path_.Append(L".xml");
    }
  }
}

void XmlDesignerDlg::OnTreeClick(NMHDR* pNMHDR, LRESULT* pResult)
{
  CPoint point;
  GetCursorPos(&point);
  CPoint pointInTree = point;
  node_tree_.ScreenToClient(&pointInTree);
  HTREEITEM item;
  UINT flag = TVHT_ONITEM;
  item = node_tree_.HitTest(pointInTree, &flag);
  if (item != NULL && pre_sel_item != item)
  {
    attr_desc_list_.DeleteAllItems();

    node_tree_.SelectItem(item);
    pre_sel_item = item;

    xml_node node = node_map[pre_sel_item];
    for (auto tmp : GLOBAL()->controller_attr_mp_[node.name()]) {
      attr_desc_list_.InsertItem(0, tmp.first.c_str());
      wstring value = node.attribute(tmp.first.c_str()).as_string();
      attr_desc_list_.SetItemText(0, 1, value.length() == 0 ? L"_" : value.c_str());
    }

    attr_desc_list_.EnableWindow(TRUE);
  }
  *pResult = 0;
}


void XmlDesignerDlg::OnTreeRClick(NMHDR* pNMHDR, LRESULT* pResult)
{
  CPoint point;
  GetCursorPos(&point);
  CPoint pointInTree = point;
  node_tree_.ScreenToClient(&pointInTree);
  HTREEITEM item;
  UINT flag = TVHT_ONITEM;
  item = node_tree_.HitTest(pointInTree, &flag);
  if (item != NULL)
  {
    node_tree_.SelectItem(item);
    //pre_sel_item = item;
    CMenu menu;
    menu.LoadMenu(IDR_MENU1);
    menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN |
      TPM_RIGHTBUTTON, point.x, point.y, this, NULL);
  }
  *pResult = 0;
}


void XmlDesignerDlg::OnTreeKeyDown(NMHDR* pNMHDR, LRESULT* pResult)
{
  LPNMTVKEYDOWN pTVKeyDown = reinterpret_cast<LPNMTVKEYDOWN>(pNMHDR);
  if (!node_tree_.GetSelectedItem())return;
  if (pTVKeyDown->wVKey == 'W' ) {  //按 W
    OnMenuMoveUp();
  } else if (pTVKeyDown->wVKey == 'S') {
    OnMenuMoveDown();
  }

  //#TODO 上下级移动
  *pResult = TRUE;
}

void XmlDesignerDlg::OnMenuDelete()
{
  HTREEITEM selItem = node_tree_.GetSelectedItem();
  if (selItem == node_tree_.GetRootItem())
    return;
  node_tree_.DeleteItem(selItem);
  xml_node node = node_map[selItem];
  node.parent().remove_child(node);
  node_map.erase(selItem);
}

void CopyChildContent(map<HTREEITEM, xml_node>& node_map, CTreeCtrl& tree_ctr, HTREEITEM& dest, HTREEITEM& sour) {
  for (HTREEITEM node = tree_ctr.GetChildItem(sour); node; node = tree_ctr.GetNextSiblingItem(node)) {
    HTREEITEM item = tree_ctr.InsertItem(tree_ctr.GetItemText(node), dest);
    node_map[item] = node_map[node];
    node_map.erase(node);
    if (tree_ctr.GetChildItem(node))
      CopyChildContent(node_map, tree_ctr, item, node);
  }
}

void XmlDesignerDlg::OnMenuMoveDown()
{
  HTREEITEM selItem = node_tree_.GetSelectedItem();
  HTREEITEM nextItem = node_tree_.GetNextSiblingItem(selItem);
  if (!nextItem)  //没有后节点
    return;
  HTREEITEM parentItem = node_tree_.GetParentItem(selItem);
  // 交换节点顺序
  CString selItemName = node_tree_.GetItemText(selItem);
  HTREEITEM item = node_tree_.InsertItem(selItemName, parentItem, nextItem);
  //交换xml节点顺序
  node_map[item] = node_map[selItem];
  node_map.erase(selItem);

  node_tree_.SelectItem(item);
  CopyChildContent(node_map, node_tree_, item, selItem);
  node_tree_.DeleteItem(selItem);
  node_map[item].parent().insert_move_after(node_map[item], node_map[nextItem]);
}

void XmlDesignerDlg::OnMenuMoveUp()
{
  HTREEITEM selItem = node_tree_.GetSelectedItem();
  HTREEITEM prevItem = node_tree_.GetPrevSiblingItem(selItem);
  if (!prevItem)  //没有前节点
    return;
  HTREEITEM parentItem = node_tree_.GetParentItem(selItem);
  HTREEITEM prevprevItem = node_tree_.GetPrevSiblingItem(prevItem);
  // 交换节点顺序
  CString selItemName = node_tree_.GetItemText(selItem);
  HTREEITEM item = node_tree_.InsertItem(selItemName, parentItem, prevprevItem ? prevprevItem : TVI_FIRST);
  node_map[item] = node_map[selItem];
  node_map.erase(selItem);
  node_tree_.SelectItem(item);
  CopyChildContent(node_map, node_tree_, item, selItem);
  node_tree_.DeleteItem(selItem);
  node_map[item].parent().insert_move_before(node_map[item], node_map[prevItem]);
}

void XmlDesignerDlg::OnMenuInsertChild()
{
  CControlChoiceDlg dlg(this);
  if (dlg.DoModal() == IDOK) {
    CString new_controller_name = dlg.choice_ctrl_name;
    HTREEITEM selItem = node_tree_.GetSelectedItem();
    selItem = selItem ? selItem : node_tree_.GetRootItem();
    xml_node node = node_map[selItem];
    HTREEITEM newItem = node_tree_.InsertItem(new_controller_name, selItem);
    node_tree_.Expand(selItem, TVE_EXPAND);
    //m_tree_ctrl.SelectItem(newItem);
    node_map[newItem] = node.append_child(new_controller_name);

    bool shouldShowConfigDlg = false;
    for (auto it : GLOBAL()->controller_attr_mp_[new_controller_name.GetBuffer()])
      if (it.first.find(L"image") != string::npos) {
        shouldShowConfigDlg = true;
        break;
      }

    if (shouldShowConfigDlg) {
      CControllerConfigDlg dlg(new_controller_name,this);
      if (dlg.DoModal() == IDOK) {  //通过向导设置
        GLOBAL()->SetXmlNodeContentByConfigData(node_map[newItem], new_controller_name.GetString());
      }
    }


    ////更多情况的定制
    //if (config_data_.ctr_name == _T("Edit")) {
    //  node_map[newItem].append_attribute("prompttext") = _T("prompt");
    //  node_map[newItem].append_attribute("textpadding") = _T("40,0,10,0");
    //  node_map[newItem].append_attribute("font") = _T("3");
    //}
  }
}


void XmlDesignerDlg::OnXmlShow()
{
  if (file_path_.IsEmpty()) {
    SetSaveFileName();
    if (file_path_.GetLength() == 0)return;
  }

  doc_.save_file(file_path_.GetBuffer());

  LPCWSTR kPreviewExePath = L"./Preview.exe";
  if (!fs::exists(kPreviewExePath)) {
    ::MessageBox(NULL,L"缺少预览进程文件,无法预览",L"error",MB_OK);
  } else {
    helper_lib::ProcessManager::RunProcess(kPreviewExePath, file_path_.GetBuffer());
  }
}

void replace_str(std::wstring& str, const std::wstring& before, const std::wstring& after)
{
  for (std::wstring::size_type pos(0); pos != std::wstring::npos; pos += after.length())
  {
    pos = str.find(before, pos);
    if (pos != std::wstring::npos)
      str.replace(pos, before.length(), after);
    else
      break;
  }
}


void XmlDesignerDlg::OnXmlGenerate()
{
  if (file_path_.IsEmpty()) {
    SetSaveFileName();
    if (file_path_.GetLength() == 0)return;
  }

  doc_.save_file(file_path_.GetBuffer());

  CFileDialog saveFileDlg(FALSE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("文件|*.*||"), NULL);
  if (saveFileDlg.DoModal() == IDOK) {
    wstring save_file_path = saveFileDlg.GetPathName().GetString();
    wstring save_file_name = fs::path{save_file_path}.filename();
    //main_frame => file_name :main_frame.h main_frame.cpp class_name:MainFrame
    if (save_file_name.empty() || save_file_name.find_first_of('_') == 0)return;

    std::wstring class_name;
    bool is_first_char_of_word_{ true };
    for (auto c : save_file_name) {
      if (c == '_') {
        is_first_char_of_word_ = true;
      } else {
        class_name.append(1, is_first_char_of_word_ ? toupper(c) : tolower(c));
        if(is_first_char_of_word_)is_first_char_of_word_ = false;
      }
    }

    fs::path p_h = save_file_path + L".h";
    fs::path p_cpp = save_file_path + L".cpp";

    std::ofstream of_h{ p_h };
    std::ofstream of_cpp{ p_cpp };

    std::ifstream if_h{ L"template.h" };
    std::ifstream if_cpp{ L"template.cpp" };
    string template_h_str, template_cpp_str;
    wstring template_h_str_w, template_cpp_str_w;

    DEFER([&] {of_h.close(); of_cpp.close(); if_h.close(); if_cpp.close(); });

    auto read_all = [](ifstream &if_s, string &str) {
      if_s.seekg(0, std::ios::end);
      str.resize((int)if_s.tellg() + 1);
      if_s.seekg(0, std::ios::beg);
      if_s.read(&str[0], str.size() - 1);
    };

    read_all(if_h, template_h_str);
    read_all(if_cpp, template_cpp_str);
    template_h_str_w = helper_lib::ToWString(template_h_str, CP_UTF8);
    template_cpp_str_w = helper_lib::ToWString(template_cpp_str, CP_UTF8);

    //替换文件名 $FILENAME$
    replace_str(template_h_str_w, L"$FILENAME$", save_file_name);
    replace_str(template_cpp_str_w, L"$FILENAME$", save_file_name);

    //替换皮肤文件名 $SKINFILENAME$
    wstring skin_file_name = fs::path{ file_path_.GetString() }.filename().c_str();
    replace_str(template_h_str_w, L"$SKINFILENAME$", skin_file_name);
    replace_str(template_cpp_str_w, L"$SKINFILENAME$", skin_file_name);

    //替换类名 $CLASSNAME$
    replace_str(template_h_str_w, L"$CLASSNAME$", class_name);
    replace_str(template_cpp_str_w, L"$CLASSNAME$", class_name);

    //替换变量声明 $MEMBERSECTION$
    CString member_section_str_h, member_section_str_cpp;
    for (auto it : node_map) {
      xml_node node = it.second;
      xml_attribute attr = node.attribute(_T("name"));
      if (!attr.empty()) {
        wstring controller_type = node.name();
        if (controller_type == L"Edit") controller_type = L"PromptEdit";
        
        member_section_str_h.AppendFormat(L"  C%sUI *%s_;\n", controller_type.c_str(), attr.value());
        member_section_str_cpp.AppendFormat(L"  FindControl(L\"%s\"), %s_));\n", attr.value(), attr.value());
      }
    }
    replace_str(template_h_str_w, L"$MEMBERSECTION$", member_section_str_h.GetString());
    replace_str(template_cpp_str_w, L"$MEMBERSECTION$", member_section_str_cpp.GetString());

    string h_write_data = helper_lib::ToString(template_h_str_w.c_str(), CP_UTF8);
    string cpp_write_data = helper_lib::ToString(template_cpp_str_w.c_str(), CP_UTF8);

    of_h.write(h_write_data.c_str(), h_write_data.size());
    of_cpp.write(cpp_write_data.c_str(), cpp_write_data.size());

    helper_lib::ProcessManager::RunProcess(L"explorer", fs::path{ save_file_path }.parent_path().c_str());
  }
}


void XmlDesignerDlg::OnAttrValueListClick(NMHDR* pNMHDR, LRESULT* pResult)
{
  LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
  CPoint point;
  GetCursorPos(&point);
  CPoint pointInTree = point;
  attr_desc_list_.ScreenToClient(&pointInTree);
  UINT flag = TVHT_ONITEM;
  int item = attr_desc_list_.HitTest(pointInTree, &flag);
  if (item >= 0) {
    attr_desc_list_.SetHotItem(item);
    CString property_name = attr_desc_list_.GetItemText(item, 0);
    CString property_val = attr_desc_list_.GetItemText(item, 1);
    property_val = property_val == L"_" ? L"" : property_val;
    name_label_.SetWindowText(property_name.GetBuffer());
    value_edit_.SetWindowText(property_val.GetBuffer());

    update_btn_.EnableWindow(TRUE);

    on_attr_value_list_sel_(node_map[pre_sel_item].name(), property_name.GetBuffer());
    value_edit_.SetFocus();
  }

  *pResult = 0;
}

void XmlDesignerDlg::OnAttrValueListKillFocus(NMHDR* pNMHDR, LRESULT* pResult)
{
  nSelItem = attr_desc_list_.GetSelectionMark();
  attr_desc_list_.SetItemState(nSelItem, LVIS_DROPHILITED, LVIS_DROPHILITED);
  *pResult = 0;
}

void XmlDesignerDlg::OnAttrValueListSetFocus(NMHDR* pNMHDR, LRESULT* pResult)
{
  attr_desc_list_.SetItemState(nSelItem, FALSE, LVIF_STATE);
  *pResult = 0;
}

void XmlDesignerDlg::OnAttrValueUpdate()
{
  CString content;
  CString property_name;
  value_edit_.GetWindowText(content);
  name_label_.GetWindowText(property_name);
  if (content.GetLength() > 0) {
    xml_attribute attr = node_map[pre_sel_item].attribute(property_name.GetBuffer());
    attr ? attr.set_value(content.GetBuffer()) : (node_map[pre_sel_item].append_attribute(property_name.GetBuffer()) = content.GetBuffer());
  } else {
    node_map[pre_sel_item].remove_attribute(property_name.GetBuffer());
  }

  for (int i = 0; i < attr_desc_list_.GetItemCount(); i++) {
    if (attr_desc_list_.GetItemText(i, 0) == property_name)
      attr_desc_list_.SetItemText(i, 1, content.GetLength() ? content.GetBuffer() : L"_");
  }

  SetFocus();
}

BOOL XmlDesignerDlg::PreTranslateMessage(MSG* pMsg)
{
  if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
    if (value_edit_.GetSafeHwnd() == ::GetFocus()) {
      OnAttrValueUpdate();
    } else {
      OnXmlShow();
    }
    return TRUE;
  }

  if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F2) {
    CString last_controller_name = GLOBAL()->last_set_controller_name_;
    if (!last_controller_name.IsEmpty()) {
      auto sel_item = node_tree_.GetSelectedItem();
      sel_item = sel_item ? sel_item : node_tree_.GetRootItem();
      xml_node node = node_map[sel_item];
      HTREEITEM newItem = node_tree_.InsertItem(last_controller_name, pre_sel_item);
      node_tree_.Expand(sel_item, TVE_EXPAND);
      node_map[newItem] = node.append_child(last_controller_name);

      GLOBAL()->SetXmlNodeContentByConfigData(node_map[newItem], last_controller_name, true);
    }
    return TRUE;
  }
  if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_F3) {
    OnMenuInsertChild();
    return TRUE;
  }

  return CDialogEx::PreTranslateMessage(pMsg);
}

